/**
 * User: alesj
 * Date: 10.1.2006
 * Time: 18:48:19
 * 
 * (C) Genera Lynx d.o.o.
 */

package com.alesj.blueberry.sar.smpp.impl;

import com.alesj.blueberry.ejb.core.CoreManager;
import com.alesj.blueberry.sar.exchange.SMPPServiceHolder;
import com.alesj.blueberry.sar.messages.MessageRepository;
import com.alesj.blueberry.sar.messages.impl.FileMessageRepository;
import com.alesj.blueberry.sar.smpp.PingDeamon;
import com.alesj.blueberry.sar.smpp.SMPPService;
import com.alesj.blueberry.spring.result.MessageInfo;
import ie.omk.smpp.Connection;
import ie.omk.smpp.Address;
import ie.omk.smpp.event.ReceiverExceptionEvent;
import ie.omk.smpp.event.ReceiverExitEvent;
import ie.omk.smpp.event.SMPPEventAdapter;
import ie.omk.smpp.message.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jndi.JndiTemplate;

import javax.naming.NamingException;

/**
 * Connection listener.
 *
 * @author <a href="mailto:ales.justin@genera-lynx.com">Ales Justin</a>
 *
 * @jmx.mbean
 * @jboss.xmbean
 */
public class MainConnectionObserver
        extends SMPPEventAdapter
        implements SMPPServiceHolder, MainConnectionObserverMBean {

    private final Log log = LogFactory.getLog(getClass());
    private SMPPService smppService;
    private MessageRepository messageRepository = new FileMessageRepository();
    private int registeredDelivery;
    private String jndiName = "blueberry/CoreManagerImpl/local";
    private CoreManager coreManager;

    // submit

    private void submitResponse(Connection source, DeliverSM deliverSM, String key, Object[] args)
            throws Exception {
        submitResponse(source, deliverSM, key, args, true);
    }

    private void submitResponse(Connection source, DeliverSM deliverSM, String key, Object[] args, boolean attempt)
            throws Exception {
        SubmitSM response = (SubmitSM)source.newInstance(SMPPPacket.SUBMIT_SM);
        response.setDestination(deliverSM.getSource());
        response.setSource(deliverSM.getSource());
        String responseMessage = messageRepository.getMessage(key, args);
        //todo alphabetEncoder
        response.setMessageText(responseMessage);
        response.setRegistered(getRegisteredDelivery());
        Connection connection = getSMPPService().getConnection();
        if (connection != null && connection.isBound()) {
            connection.sendRequest(response); //it is actually a request to smsc
        } else {
            log.error("Connection not bound or null.");
            if (attempt) {
                smppService.notifyOfFailure(null);
                submitResponse(source, deliverSM, key, args, false);
            }
        }
    }

    // smpp adapter

    public void deliverSM(Connection source, DeliverSM dm) {
        String messageId = dm.getMessageId();
        String number = dm.getSource().getAddress();
        String messageText = dm.getMessageText();
        log.info("Receieved messageText [" + messageId + ", " + number + "]: " + messageText);
        if (dm.getRegistered() > 0) {
            log.info("Updating delivery request entry [" + messageId + ", " + number + "]");
            getCoreManager().updateDelivery(messageId, number);
        } else {
            boolean responseSubmited = false;
            try {
                // process message
                MessageInfo messageInfo = getCoreManager().processRequest(messageId, number, messageText);                
                submitResponse(source, dm, messageInfo.getKey(), messageInfo.getArgs());
                responseSubmited = true;
                log.info("Response submited ok [" + messageId + ", " + number + "]");
            } catch (SMPPOutputExhangeException e) {
                log.error(e.getMessage());
            } catch (Exception e) {
                log.warn("Exception processing request[" + messageId + ", " + number + "]: " + e.getMessage());
                try {
                    getCoreManager().saveErrorLog(messageId, number, e.getMessage());
                    submitResponse(source, dm, MessageInfo.FAILURE_KEY, new Object[]{});
                    responseSubmited = true;
                } catch (Throwable t) {
                    log.error("Failed to send failure response[" + messageId + ", " + number + "]: " + t.getMessage());
                }
            } finally {
                if (!responseSubmited) {
                    getCoreManager().saveErrorLog(messageId, number, "Could not send response.");
                }
            }
        }
    }

    public void submitSMResponse(Connection source, SubmitSMResp smr) {
        Address sourceAddress = smr.getSource();
        String messageId = smr.getMessageId();
        String number = null;
        if (sourceAddress != null) {
            number = sourceAddress.getAddress();
        }
        log.info("Updating submit request entry [" + messageId + ", " + number + "]");
        getCoreManager().updateSubmit(messageId, number);
    }

    public void genericNack(Connection source, GenericNack nack) {
        log.warn("GenericNack received: " + nack);
    }

    public void queryLinkResponse(Connection source, EnquireLinkResp elr) {
        PingDeamon pingDeamon = getSMPPService().getPingDeamon();
        if (pingDeamon != null) {
            pingDeamon.responseReceived(elr);
        } else {
            log.info("No ping deamon defined ...");
        }
    }

    public void receiverExitException(Connection source, ReceiverExitEvent rev) {
        getSMPPService().notifyOfFailure(rev.getException());
    }

    public void receiverException(Connection source, ReceiverExceptionEvent rev) {
        log.warn("Network communications error or possibly an unidentified" +
                " incoming packet type from the SMSC: " + rev.getException());
    }

    // helper

    public SMPPService getSMPPService() {
        return smppService;
    }

    public void setSMPPService(SMPPService smppService) {
        this.smppService = smppService;
    }

    // mbeans

    /**
     * @jmx.managed-attribute
     */
    public int getRegisteredDelivery() {
        return registeredDelivery;
    }

    /**
     * @jmx.managed-attribute
     */
    public void setRegisteredDelivery(int registeredDelivery) {
        this.registeredDelivery = registeredDelivery;
    }

    /**
     * @jmx.managed-attribute
     */
    public String getJndiName() {
        return jndiName;
    }

    /**
     * @jmx.managed-attribute
     */
    public void setJndiName(String jndiName) {
        this.jndiName = jndiName;
        coreManager = null; //to lookup for new core manager proxy
    }

    /**
     * @jmx.managed-attribute
     */
    public String getMessageRepositoryClass() {
        return messageRepository.getClass().getName();
    }

    /**
     * @jmx.managed-attribute
     */
    public void setMessageRepositoryClass(String messageRepositoryClass) throws Exception {
        messageRepository = (MessageRepository)Class.forName(messageRepositoryClass).newInstance();
    }

    // jndi ejb lookup

    private synchronized CoreManager getCoreManager() {
        if (coreManager == null) {
            try {
                coreManager = (CoreManager)new JndiTemplate().lookup(getJndiName(), CoreManager.class);
            } catch (NamingException e) {
                throw new RuntimeException(e);
            }
        }
        return coreManager;
    }

}
